/* **************************************************** */
/* file misc.c:  contains pattern manipulation routines */
/*               and miscellaneous other functions.     */
/*                                                      */
/* Copyright (c) 1990-1998 by Donald R. Tveter          */
/*                                                      */
/* **************************************************** */

#include "cpn.h"

/* built-in function */

extern int rand();

/* homemade functions */

extern int pg(FILE *,char *);
extern void saveweights();
extern void printnetsize();
extern void restoreweights();
extern WTTYPE rdr(int,REAL,int);
extern REAL readchar();
extern int printoutunits(int,LAYER *,int,REAL,char,char);
extern UNIT *locateunit();
extern void klearn();
extern void glearn();
extern void forward();

extern char *datafile,emptystring,informat,outstr[];
extern char probtype, ringbell, summary, *testfile;
extern char *trainfile;
extern char seednote, runningflag, outformat;
extern char trfiles[], wtinitroutine;
extern short nlayers;
extern int lastprint,lastsave,prevnpats,readerror;
extern int readingpattern,right,totaliter,wrong;
extern int wttotal, wtsinuse;
extern SEEDNODE *seedstart;
extern REAL unknown, toler, initrange, totaldiff;
extern LAYER *last, *start;
extern DATA s[2][2], rs[2][2];
extern REAL eta, eta2;

void nullpatterns(int list)
{ /* dispose of any patterns before reading more */
PATLIST *pl, *nextpl;
PATNODE *p;

if (start->patstart[list] != NULL)
 {
  pl = start->patstart[list];
  while (pl != NULL)
   {
    nextpl = pl->next;
    p = pl->pats;
    free(p);
    pl = nextpl;
   };
  pl = last->patstart[list];
  while (pl != NULL)
   {
    nextpl = pl->next;
    p = pl->pats;
    free(p);
    pl = nextpl;
   };
 };
start->patstart[list] = NULL;
last->patstart[list] = NULL;
s[TOL][list].npats = 0;
s[MAX][list].npats = 0;
prevnpats = 0;
}

void resetpats(int list)
{
start->currentpat[list] = NULL;
last->currentpat[list] = NULL;
}

void findendofpats(LAYER *layer)
/* purpose is to set all layer->currentpat */
/* fields to end of pattern list so more   */
/* patterns can be added at the end.       */
{
PATLIST *pl;
pl = (PATLIST *) layer->patstart[TRAIN];
while (pl->next != NULL) pl = pl->next;
layer->currentpat[TRAIN] = pl;
}

int loadpat()
{
  REAL val;
  int unitnumber;
  UNIT *otherunit, *hunit, *u;
  readingpattern = 1;
  hunit = start->next->units;
  u = start->units;
  while (u != NULL)
   {
    if (informat == 'r') val = rdr(GE,(REAL) -MAXINT,'0');
    else val = scale(readchar());
    if (readerror != 0) goto errorexit;
    u->oj = val;
    u = u->next;
   };

 readingpattern = 0;
 forward();
 return(1);

errorexit:
 readingpattern = 0;
 return(0);
}

void nextpat(int list)
{
if (start->currentpat[list] == NULL)
 {
  start->currentpat[list] = start->patstart[list];
  last->currentpat[list] = last->patstart[list];
 }
else
 {
  start->currentpat[list] = (start->currentpat[list])->next;
  last->currentpat[list] = (last->currentpat[list])->next;
 };
}

void setinputpat(int list)
{
register PATNODE *pn;
register UNIT *u;
PATLIST *pl;
  
pl = start->currentpat[list];
pn = pl->pats;
u = (UNIT *) start->units;
while (u != NULL)
 {
  if (pn->addr == NULL)  /* load a plain number */
   {
    u->oj = pn->val;
    pn++;
   }
  u = u->next;
 };
}

void setoutputpat(int list)
{
 PATLIST *pl;
 PATNODE *targets;
 UNIT *u;
 pl = last->currentpat[list];
 targets = pl->pats;
 u = (UNIT *) last->units;
 while (u != NULL)
  {
   u->tj = targets->val;
   targets++;
   u = u->next;
  };
}

void setonepat(int list) /* set input pattern */
{
register UNIT *u;
register LAYER *innerlayers;

setinputpat(list);
setoutputpat(list);
innerlayers = start->next;
while (innerlayers->next != NULL)
 {  /* set errors on the inner layer units to 0 */
  u = (UNIT *) innerlayers->units;
  while (u != NULL)
   {
    u->error = 0;
    u = u->next;
   };
  innerlayers = innerlayers->next;
 };
}

void clear()
{
LAYER *p, *lowerlayer;
UNIT *u, *unext, *ulast, *bu;
WTNODE *w, *wnext, *wprev;
int i,j, layerno;
REAL *val;

for (i=TRAIN;i<=TEST;i++) for (j=TOL;j<=MAX;j++)
 {
  s[j][i].iterno = 0;
  s[j][i].right = 0;
  s[j][i].wrong = s[j][i].npats;
  s[j][i].avgerr = 0.0;
  s[j][i].pctright = 0.0;
 };
totaliter = 0;
lastsave = 0;
lastprint = 0;

/* clear the input layer units */
u = start->units;
while (u != NULL)
 {
  u->oj = 0;
  u = u->next;
 };
/* re-initialize the weights and such */
wttotal = 0;
p = start->next;
while (p != NULL)
 {
  u = (UNIT *) p->units;
  while (u != NULL)
   {
    u->oj = 0;
    w = (WTNODE *) u->wtlist;
    while (w != NULL)
     {
      w->weight = 0;
      wttotal = wttotal + 1;
      w = w->next;
     };
    u = u->next;
   };
  p = p->next;
 };
wtsinuse = wttotal;
}

void stats()
{
 right = s[TOL][TRAIN].npats - wrong;
 s[TOL][TRAIN].right = right;
 s[TOL][TRAIN].wrong = wrong;
 s[TOL][TRAIN].iterno = totaliter;
 s[MAX][TRAIN].iterno = totaliter;
 s[TOL][TRAIN].avgerr =
    unscaleint(totaldiff) / (REAL) ((right + wrong) * last->unitcount);
 s[MAX][TRAIN].avgerr = s[TOL][TRAIN].avgerr;
 s[TOL][TRAIN].pctright = 100.0 *
    (REAL) right / (REAL) (right + wrong);
 if (probtype == 'c') s[MAX][TRAIN].pctright = s[MAX][TRAIN].right
    * 100.0 / (s[MAX][TRAIN].right + s[MAX][TRAIN].wrong);
}

int printstats(FILE *writefile,int list,int fileid,DATA s[2][2])
{
char *f;
int patcount;

if (fileid == 1)
 {
  if (list == TEST) f = testfile;
  else if (*trainfile == emptystring) f = datafile;
  else f = trfiles;
  patcount = s[TOL][list].npats;
  if (list == TRAIN) {if (pg(writefile,trfiles)) return(1);}
  else
   {
    pg(writefile,"tf ");
    pg(writefile,testfile);
    if (pg(writefile,"\n")) return(1);
   };
 };

lastprint = totaliter;
if (writefile != stdout) pg(writefile,"*");
sprintf(outstr,"%5d",s[TOL][list].iterno); pg(writefile,outstr);
pg(writefile,"    ");
pg(writefile,"(TOL) ");
sprintf(outstr,"%6.2f %% ",s[TOL][list].pctright); pg(writefile,outstr);
sprintf(outstr,"(%1d right  ",s[TOL][list].right);pg(writefile,outstr);
sprintf(outstr,"%1d wrong)",s[TOL][list].wrong); pg(writefile,outstr);
sprintf(outstr,"  %7.5f err/unit\n",s[TOL][list].avgerr);
if (pg(writefile,outstr)) return(1);
if (probtype == 'g') return(0);

lastprint = totaliter;
if (writefile != stdout) pg(writefile,"*");
sprintf(outstr,"%5d",s[MAX][list].iterno); pg(writefile,outstr);
pg(writefile,"    ");
pg(writefile,"(MAX) ");
sprintf(outstr,"%6.2f %% ",s[MAX][list].pctright); pg(writefile,outstr);
sprintf(outstr,"(%1d right  ",s[MAX][list].right); pg(writefile,outstr);
sprintf(outstr,"%1d wrong)",s[MAX][list].wrong); pg(writefile,outstr);
sprintf(outstr,"  %7.5f err/unit\n",s[MAX][list].avgerr);
if (pg(writefile,outstr)) return(1); else return(0);
}

void maxerrors()
{
register UNIT *u;
register short unitnumber;
register REAL largest;
register short largestunit, answer;
register PATNODE *targetptr;
PATLIST *pl;

largest = scale(-30.0);
largestunit = 0;
unitnumber = 1;
pl = last->currentpat[TRAIN];
targetptr = pl->pats;
u = last->units;
while (u != NULL)
 {
  if (targetptr->val > 0) answer = unitnumber;
  if (u->oj > largest)
   {
    largest = u->oj;
    largestunit = unitnumber;
   };
  unitnumber++;
  targetptr++;
  u = u->next;
 };
if (answer == largestunit) s[MAX][TRAIN].right++;
else s[MAX][TRAIN].wrong++;
}

void errors(int list,REAL *rowsum,char *tok,char *mok,int *answer,int *largestunit)
{
register WTTYPE absdiff;
register WTTYPE sum;
register UNIT *u;
WTTYPE largest;
PATNODE *targetptr;
int notclose, unitnumber;
PATLIST *pl;

notclose = last->unitcount;
if (probtype == 'c')
 {
  unitnumber = 0;
  largest = scale(-31.0);
  *largestunit = 0;
 };
sum = 0;
pl = last->currentpat[list];
targetptr = pl->pats;
u = last->units;
while (u != NULL)
 {
  u->tj = targetptr->val;
  targetptr++;
  if (probtype == 'c')
   {
    unitnumber = unitnumber + 1;
    if (u->tj > 0) *answer = unitnumber;
    if (u->oj > largest)
     {
      largest = u->oj;
      *largestunit = unitnumber;
     };
   }; /* end if (probtype ... */
  absdiff = u->tj - u->oj;

  if (absdiff < 0) absdiff = -absdiff;
  if (absdiff < toler) notclose = notclose - 1;
  sum = sum + absdiff;
/*sprintf(outstr,"sum = %d\n",sum); pg(stdout,outstr);*/
  u = u->next;
 }; /* end while (u != NULL) */
*rowsum = unscaleint(sum);
/*sprintf(outstr,"*rowsum = %f\n",*rowsum); pg(stdout,outstr);*/
if (notclose == 0) *tok = 1; else *tok = 0;
if (probtype == 'c')
 {if (*answer == *largestunit) *mok = 1; else *mok = 0;}
else *mok = 0;
}

int evalone(int patnum,int list,int printing)
{
int i,answer,largestunit;
REAL rowsum;
char tok,mok;

resetpats(list);
for (i=1;i<=patnum;i++) nextpat(list);
setonepat(list);
forward();
errors(list,&rowsum,&tok,&mok,&answer,&largestunit);
if (printing)
 {if (printoutunits(patnum,last,list,rowsum,tok,mok)) return(1);}
else return(0);
}

int eval(int list,int printing)
{ 
int i, j, patcount, theclass, best, nclasses, ipct, *addr, patnum;
REAL sum, rowsum;
char mok,tok;

sum = 0.0;
s[TOL][list].iterno = totaliter;
s[TOL][list].right = 0;
s[TOL][list].wrong = 0;
if (probtype == 'c')
 {
  s[MAX][list].iterno = totaliter;
  s[MAX][list].right = 0;
  s[MAX][list].wrong = 0;
  nclasses = last->unitcount;
 };
patcount = s[TOL][list].npats;
resetpats(list);
for (i=1;i<=s[TOL][list].npats;i++)
 {
  nextpat(list);
  setonepat(list);
  forward();
  errors(list,&rowsum,&tok,&mok,&theclass,&best);
  sum = sum + rowsum;
  if (tok) s[TOL][list].right++; else s[TOL][list].wrong++;
  if (probtype == 'c')
   {
    if (mok) s[MAX][list].right++; else s[MAX][list].wrong++;
   };
  if (printing)
   {if (printoutunits(i,last,list,rowsum,tok,mok)) return(1);};
 };  /* end for */
s[TOL][list].pctright = 100.0 *
   (REAL) s[TOL][list].right / (REAL) patcount;
s[TOL][list].avgerr = sum / ((REAL) patcount * last->unitcount);
s[MAX][list].avgerr = s[TOL][list].avgerr;
if (probtype == 'c')
 {
  s[MAX][list].pctright = 100.0 *
     (REAL) s[MAX][list].right / (REAL) patcount;
  s[MAX][list].avgerr = sum / ((REAL) patcount * last->unitcount);
  s[TOL][list].avgerr = s[MAX][list].avgerr;
 };
return(0);
}

int compactstats(DATA s[2][2])
{ /* training patterns */
 eval(TRAIN,0);
 sprintf(outstr,"%5d ",totaliter); pg(stdout,outstr);
 pg(stdout,"   ");
 sprintf(outstr,"%6.2f %% ",s[TOL][TRAIN].pctright); pg(stdout,outstr);
 if (probtype == 'c')
  {
   sprintf(outstr,"%6.2f %% ",s[MAX][TRAIN].pctright); pg(stdout,outstr);
  };
 sprintf(outstr,"%7.5f ",s[TOL][TRAIN].avgerr); pg(stdout,outstr);
 pg(stdout,"  ");
 if (s[TOL][TEST].npats > 0)  /* if testing patterns exist */
  {
   if (s[TOL][TEST].iterno != totaliter) eval(TEST,0);
   sprintf(outstr,"  %6.2f %% ",s[TOL][TEST].pctright); pg(stdout,outstr);
   if (probtype == 'c')
    {
     sprintf(outstr,"%6.2f %% ",s[MAX][TEST].pctright); pg(stdout,outstr);
    };
   sprintf(outstr,"%7.5f ",s[TOL][TEST].avgerr); pg(stdout,outstr);
  };
 if (s[TOL][TRAIN].pctright == 100.0)
  {
   pg(stdout," DONE");
   if (ringbell == '+') putchar(7);
  };
 if(pg(stdout,"\n")) return(1); else return(0);
}
 
void kick(WTTYPE size,WTTYPE amount)
/* give the network a kick */
{ 
LAYER *layer;
UNIT *u;
WTNODE *w;
WTTYPE value, delta;
int sign;

layer = start->next;
while (layer != NULL)
 {
  u = (UNIT *) layer->units;
  while (u != NULL)
   {
    w = (WTNODE *) u->wtlist;
    while (w != NULL)
     {
      value = w->weight;
      if (value != 0) sign = 1;
      else if ((rand() & 32767) > 16383) sign = -1;
      else sign = 1;
      delta = (INT32) sign * amount * (rand() & 32767) / 32768;
      if (value >= size) value = value - delta;
      else if (value < -size) value = value + delta;
      w->weight = value;
      w = w->next;
     }
    u = u->next;
   }
  layer = layer->next;
 } 
}

void oneset() /* go through the patterns once and update weights */
{ int i;
  LAYER *layer;
  register UNIT *u;
  register WTNODE *w;
  short numbernotclose;

 wrong = s[TOL][TRAIN].npats;
 s[MAX][TRAIN].right = 0;
 s[MAX][TRAIN].wrong = 0;
 resetpats(TRAIN);
 for(i=1;i<=s[TOL][TRAIN].npats;i++)
  {
   nextpat(TRAIN);
   setonepat(TRAIN);
   klearn();
   glearn();
  }; /* end for i */
  eval(TRAIN,0);
  wrong = s[TOL][TRAIN].wrong;
 totaliter = totaliter + 1;
}

int run(int n,int prpatsrate)
/* the number of iterations to run */
/* rate at which to print output patterns */
{
int i, wtlimitbefore;

if (runningflag == '+') {if (pg(stdout,"running . . .\n")) return(1);};
if (totaliter == 0) eval(TRAIN,0);
for (i=1;i<=n;i++)
 {
  totaldiff = 0;
  oneset();
  if ((i % prpatsrate == 0) || (i == n) || (wrong == 0))
   {
    if (compactstats(s)) return(1);
   };
  if (s[TOL][TRAIN].wrong == 0) return(0);
#ifndef UNIX
  if (kbhit() && getch() == 27 /* escape key */) return(1);
#endif
 };
return(0);
} 
